var class2type = {};

var toString = class2type.toString;

var hasOwn = class2type.hasOwnProperty;

var fnToString = hasOwn.toString;

var getProto = Object.getPrototypeOf;


/**
    1. $.extend的用法。 第一个参数是决定是否需要深复制。 由 true, false。 默认是浅复制
    params:
    options => 接收传递过来的arguments 的中间参数。
    name =>  没对对象的key值。
    src =>   当传递的对象key值相同到时候。要和并
    copy =>   复制的value 值。
    copyIsArray =>  判断value对象是不是数组。
    clone =>    当深度复制的时候。需要新建一个变量。不会改变原始的值。
    target =>  最开始是默认值是取传进来的第一个参数。过后往后一直跳。$.extend(a,b,c); target 为 a, 为b,为c。
    i =>  决定当前target 等于参数中的某个值
    length =>
    deep => 默认是false 是决定是否需要深复制的参数。 true 是深复制。 false 是浅复制
*/
const extend = () => {
  var options, name, src, copy, copyIsArray, clone,
    target = arguments[0] || {},
    i = 1,
    length = arguments.length,
    deep = false;

  // 判断 target 来决定是否 深复制
  if (typeof target === "boolean") {
    deep = target;

    //当深复制的时候，需要跳过第一个参数。 来循环传进来的参数。 刚开始 1,2,3
    target = arguments[i] || {};
    i++;

  }

  // 当 target 不是一个对象且 不是一个函数对象 这个时候就把target 变为空对象。
  if (typeof target !== "object" && !isFunction(target)) {
    target = {};
  }

  // 如果传递过来的参数只有一个的情况下。target 就是该参数。
  if (i === length) {
    target = this;
    i--; // 把i 改为1
  }

  for (; i < length; i++) {

    // 处理非空 或者 undefined 的情况。   null == null 。 undefined == null;
    //options  是来接收除了第一个参数外的所有参数。中间桥梁的作用吧
    if ((options = arguments[i]) != null) {

      // name in options 决定了 传过来的值必须是对象。如果不是对象的话。将要被拆分
      for (name in options) {

        //这个是决定后面同级 的key 值， 后面将要覆盖前面的。
        src = target[name];

        //获取 key值为 name 的 value值。
        copy = options[name];

        // Prevent never-ending loop 阻止无限循环。 ？？？没有搞懂，设么情况下才会出现这种情况。

        if (target === copy) {
          continue;
        }

        /**

        1.深复制，且value 为对象属性的时候执行递归复制。 isPlainObect(copy) 判断copy第不是一个 纯对象
        2.copy 时一个数组的时候。执行里面的函数。
        3.(因为只有对象value 和 数组value 是需要进行深复制的。)
        */
        if (deep && copy && (isPlainObject(copy) ||
            (copyIsArray = Array.isArray(copy)))) {

          if (copyIsArray) {

            copyIsArray = false;
            clone = src && Array.isArray(src) ? src : [];

          } else {

            clone = src && isPlainObject(src) ? src : {};
          }

          // 创建新对象。来进行深度复制。

          target[name] = extend(deep, clone, copy);

          //除了对象数组，和undefined 的值。
        } else if (copy !== undefined) {
          target[name] = copy;
        }
      }
    }
  }

  // Return the modified object
  return target;
};



const isFunction = function isFunction(obj) {

  // Support: Chrome <=57, Firefox <=52
  // In some browsers, typeof returns "function" for HTML <object> elements
  // (i.e., `typeof document.createElement( "object" ) === "function"`).
  // We don't want to classify *any* DOM node as a function.
  return typeof obj === "function" && typeof obj.nodeType !== "number";
};



const isPlainObject = function(obj) {
  var proto, Ctor;

  // Detect obvious negatives
  // Use toString instead of jQuery.type to catch host objects
  if (!obj || toString.call(obj) !== "[object Object]") {
    return false;
  }

  proto = getProto(obj);

  // Objects with no prototype (e.g., `Object.create( null )`) are plain
  if (!proto) {
    return true;
  }

  // Objects with prototype are plain iff they were constructed by a global Object function
  Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
  return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
};



export default extend;